#include "elinput.h"
#include <QLineEdit>
#include <QPaintEvent>
#include <QPainter>
#include "../basic/elbutton.h"
#include "../basic/eltheme.h"

const int ElInput::Editor_Width = 200;
const int ElInput::Init_Height = 32;
const int ElInput::Padding_Lr = 7;
const int ElInput::Padding_Tb = 3;
const int ElInput::Prefix_Spacing = 6;
const int ElInput::Suffix_Spacing = 6;

ElInput::ElInput(QWidget *parent)
    : ElComponent{parent}
{
    _lineEdit = new QLineEdit(this);
    _lineEdit->setFrame(false);
    _lineEdit->installEventFilter(this);
    connect(_lineEdit, &QLineEdit::textChanged, this, [=] { update(); });
    setAttribute(Qt::WA_Hover);
    initPalette();
}

ElInput::ElInput(const QString &text, QWidget *parent)
    : ElInput{parent}
{
    setText(text);
}

QLineEdit *ElInput::editor() const
{
    return _lineEdit;
}

int ElInput::maxLength() const
{
    return _lineEdit->maxLength();
}

void ElInput::setMaxLength(int l)
{
    _lineEdit->setMaxLength(l);
}

int ElInput::minLength() const
{
    return _minLen;
}

void ElInput::setMinLength(int l)
{
    _minLen = l;
}

bool ElInput::eventFilter(QObject *watched, QEvent *e)
{
    if (watched == _lineEdit) {
        if (e->type() == QEvent::FocusIn) {
            _focusIn = true;
            _palette.toggleColorGroup(ElPalette::Pressed);
            update();
        } else if (e->type() == QEvent::FocusOut) {
            _focusIn = false;
            _palette.toggleColorGroup(ElPalette::Normal);
            update();
        }
    }
    return ElComponent::eventFilter(watched, e);
}

bool ElInput::event(QEvent *e)
{
    if (e->type() == QEvent::EnabledChange) {
        _palette.toggleColorGroup(isEnabled() ? ElPalette::Normal : ElPalette::Disabled);
        update();
    }
    return ElComponent::event(e);
}

void ElInput::paintEvent(QPaintEvent *e)
{
    auto w = Editor_Width + 2 * Padding_Lr;
    int prepend_w = 0, append_w = 0, prefix_w = 0, suffix_w = 0;
    if (_prepend != nullptr) {
        prepend_w = _prepend->width();
        w += prepend_w;
    }
    if (_append != nullptr) {
        append_w = _append->width();
        w += append_w;
    }
    if (_prefix != nullptr) {
        prefix_w = _prefix->width();
        w += prefix_w + Prefix_Spacing;
    }
    if (_suffix != nullptr) {
        suffix_w = _suffix->width();
        w += suffix_w + Suffix_Spacing;
    }
    // be careful showPwdIcon being true doesn't mean _pwdBtn isn't null.
    // so don't judge condition by showPwdIcon
    bool hasPwdIcon = _pwdBtn != nullptr;
    if (hasPwdIcon) {
        w += _pwdBtn->width() + Suffix_Spacing;
    }
    QPainter painter(this);
    // showWordLimit
    QRectF word_limit_br;
    if (_showWordLimit) {
        auto limitTip = QString("%1/%2").arg(_lineEdit->text().length()).arg(maxLength());
        auto font = painter.font();
        font.setPointSize(8);
        painter.setFont(font);
        painter.drawText(QRectF(), Qt::AlignCenter, limitTip, &word_limit_br);
        w += word_limit_br.width() + Suffix_Spacing;
    }
    resize(w, Init_Height);

    auto drawRect = getDrawRect();

    painter.setPen(_palette.color(ElPalette::Border));
    painter.setBrush(_palette.color(ElPalette::Background));
    painter.drawRoundedRect(drawRect, double(height()) / 6.0, double(height()) / 6.0);

    // show append and prepend
    // border color doesn't change with colorGroup
    painter.setPen(_palette.color(ElPalette::Normal, ElPalette::Border));
    if (_prepend != nullptr) {
        _prepend->move(drawRect.topLeft().toPoint());
        _prepend->show();
        // draw line after prepend
        painter.drawLine(prepend_w, 0, prepend_w, height());
        auto temp = dynamic_cast<ElComponent *>(_prepend);
        if (temp != nullptr)
            painter.fillRect(QRectF(prepend_w - 10, 1, 10, height() - 2),
                             temp->palette().color(ElPalette::Background));
    }
    if (_append != nullptr) {
        _append->move(drawRect.right() - _append->width(), drawRect.top());
        _append->show();
        // draw line before append
        painter.drawLine(width() - append_w, 0, width() - append_w, height());
        auto temp = dynamic_cast<ElComponent *>(_append);
        if (temp != nullptr)
            painter.fillRect(QRectF(width() - append_w, 1, 10, height() - 2),
                             temp->palette().color(ElPalette::Background));
    }
    // show prefix
    if (_prefix != nullptr) {
        _prefix->move(prepend_w + Padding_Lr, (height() - _prefix->height()) / 2);
        _prefix->show();
    }
    // show pwdIcon if showPwdIcon is true
    if (hasPwdIcon) { // show when focusing in
        if (_focusIn) {
            _pwdBtn->move(width() - append_w - Padding_Lr - _pwdBtn->width()
                              - (_showWordLimit ? word_limit_br.width() + Suffix_Spacing : 0),
                          (height() - _pwdBtn->height()) / 2);
            _pwdBtn->show();
        } else {
            _pwdBtn->hide();
        }
    }
    // show suffix
    if (_suffix != nullptr) {
        _suffix->move(width() - append_w - Padding_Lr - suffix_w
                          - (_showWordLimit ? word_limit_br.width() + Suffix_Spacing : 0)
                          - (hasPwdIcon && _focusIn ? _pwdBtn->width() + Suffix_Spacing : 0),
                      (height() - _suffix->width()) / 2);
        _suffix->show();
    }
    // draw word limit
    if (_showWordLimit) {
        auto left = width() - append_w - Padding_Lr - word_limit_br.width();
        QRectF drawingRect(left, 0, word_limit_br.width(), height());
        painter.setPen(ThemeColor::textDisabledColor);
        auto limitTip = QString("%1/%2").arg(_lineEdit->text().length()).arg(maxLength());
        painter.drawText(drawingRect, Qt::AlignCenter, limitTip);
    }
    // show lineEdit
    _lineEdit->resize(Editor_Width, height() - 2 * Padding_Tb);
    _lineEdit->move(prepend_w + Padding_Lr + prefix_w + Suffix_Spacing, Padding_Tb);
    e->accept();
}

void ElInput::enterEvent(QEnterEvent *e)
{
    if (!isEnabled() || _palette.currentColorGroup() == ElPalette::Pressed) {
        e->accept();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Entered); // Qt::WA_Hover is set so update automatically
    e->accept();
}

void ElInput::leaveEvent(QEvent *e)
{
    if (!isEnabled() || _palette.currentColorGroup() == ElPalette::Pressed) {
        e->accept();
        return;
    }
    _palette.toggleColorGroup(ElPalette::Normal); // Qt::WA_Hover is set so update automatically
    e->accept();
}

void ElInput::initPalette()
{
    _palette.setColor(ElPalette::Border, ThemeColor::textDisabledColor);
    _palette.setColor(ElPalette::Entered, ElPalette::Border, ThemeColor::infoColor);
    // pressed means focused in
    _palette.setColor(ElPalette::Pressed, ElPalette::Border, ThemeColor::primaryColor);
    _palette.setColor(ElPalette::Disabled, ElPalette::Background, ThemeColor::inputDisabledColor);
    _palette.setColor(ElPalette::Disabled, ElPalette::Border, ThemeColor::textDisabledColor);
}

bool ElInput::isAutoFocus() const
{
    return _autoFocus;
}

void ElInput::setAutoFocus(bool autoFocus)
{
    if (_autoFocus == autoFocus)
        return;
    _autoFocus = autoFocus;
    _lineEdit->setFocusPolicy(_autoFocus ? Qt::StrongFocus : Qt::ClickFocus);
}

void ElInput::createPwdBtn()
{
    _pwdBtn = new ElTextButton(this);
    auto isNormal = _lineEdit->echoMode() == QLineEdit::Normal;
    _pwdBtn->setIcon(isNormal ? ":/icons/view_off.svg" : ":/icons/view.svg");
    _pwdBtn->setClipped(true);
    // change style
    auto pal = _pwdBtn->palette();
    pal.setColor(ElPalette::Entered, ElPalette::Foreground, ThemeColor::textPrimaryColor);
    pal.setColor(ElPalette::Pressed, ElPalette::Foreground, ThemeColor::textPrimaryColor);
    _pwdBtn->setPalette(pal);
    connect(_pwdBtn, &ElTextButton::clicked, this, [=] {
        auto isNormal = _lineEdit->echoMode() == QLineEdit::Normal;
        _pwdBtn->setIcon(isNormal ? ":/icons/view.svg" : ":/icons/view_off.svg");
        _lineEdit->setEchoMode(isNormal ? QLineEdit::Password : QLineEdit::Normal);
    });
}

bool ElInput::showPwdIcon() const
{
    return _showPwdIcon;
}

void ElInput::setShowPwdIcon(bool b)
{
    if (_showWordLimit) {
        qDebug() << tr("[ElInput]Could not set showPwdIcon, because you are trying to show the "
                       "length of a password!");
        return;
    }
    if (_lineEdit->echoMode() != QLineEdit::Normal && _lineEdit->echoMode() != QLineEdit::Password) {
        qDebug() << tr("[ElInput]Could not set showPwdIcon, because editor's echoMode should be "
                       "Normal or Password.");
        return;
    }
    if (_showPwdIcon == b)
        return;
    _showPwdIcon = b;
    if (_showPwdIcon) {
        createPwdBtn();
    } else {
        delete _pwdBtn;
        _pwdBtn = nullptr;
    }
}

bool ElInput::showWordLimit() const
{
    return _showWordLimit;
}

void ElInput::setShowWordLimit(bool b)
{
    if (_showPwdIcon) {
        qDebug() << tr("[ElInput]Could not set showWordLimit, because you are trying to show the "
                       "length of a password!");
        return;
    }
    if (_showWordLimit == b)
        return;
    _showWordLimit = b;
}

QString ElInput::text() const
{
    return _lineEdit->text();
}

void ElInput::setText(const QString &text)
{
    _lineEdit->setText(text);
    emit _lineEdit->textChanged(text);
}

QWidget *ElInput::prefix() const
{
    return _prefix;
}

void ElInput::setPrefix(QWidget *prefix)
{
    if (_prefix == prefix)
        return;
    delete _prefix;
    _prefix = prefix;
    _prefix->setParent(this);
    auto threshold = height() - 2 * Padding_Tb;
    if (_prefix->height() > threshold)
        _prefix->resize(_prefix->width(), height() - 2 * Padding_Tb);
}

QWidget *ElInput::suffix() const
{
    return _suffix;
}

void ElInput::setSuffix(QWidget *suffix)
{
    if (_suffix == suffix)
        return;
    delete _suffix;
    _suffix = suffix;
    _suffix->setParent(this);
    auto threshold = height() - 2 * Padding_Tb;
    if (_suffix->height() > threshold)
        _suffix->resize(_suffix->width(), threshold);
}

QWidget *ElInput::append() const
{
    return _append;
}

void ElInput::setAppend(QWidget *append)
{
    if (_append == append)
        return;
    delete _append;
    _append = append;
    _append->setParent(this);
    _append->resize(_append->width(), height() + 2);
}

QWidget *ElInput::prepend() const
{
    return _prepend;
}

void ElInput::setPrepend(QWidget *prepend)
{
    if (_prepend == prepend)
        return;
    delete _prepend;
    _prepend = prepend;
    _prepend->setParent(this);
    _prepend->resize(_prepend->width(), height() + 2);
}
